package com.yuhan.gate.filter;

import com.google.gson.Gson;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 对请求json参数进行防sql注入过滤
 * @author: hejing
 * @create: 2019/7/25
 **/
@Component
@RefreshScope
public class MyFiter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(MyFiter.class);


    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 自定义过滤器执行的顺序，数值越大越靠后执行，越小就越先执行
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 2;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 执行过滤逻辑
     *
     * @return
     */
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse res = ctx.getResponse();
        try {
            res.setHeader("Access-Control-Allow-Origin", "*");
            res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,DELETE,PUT,HEAD");
            res.setHeader("Access-Control-Allow-Headers",
                    "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With");
            if (!ContentType.APPLICATION_JSON.getMimeType().equalsIgnoreCase(request.getContentType())) {
                return null;
            }
            log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
            //对application/json数据格式的数据进行去空格处理
            InputStream in = ctx.getRequest().getInputStream();
            String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
            if (body != null && !"".equals(body)) {
                if (!body.startsWith("{") && body.endsWith("}")) {
                    return null;
                }
                if (!body.contains("[") && body.contains("]")) {
                    return null;
                }
            }
            Map<String, Object> stringObjectMap = cleanXSS(body);
            JSONObject json = JSONObject.fromObject(stringObjectMap);
            String newBody = json.toString();
            log.info(String.format(">>>>>>newBody", newBody));
            //	如果存在sql注入,直接拦截请求(特殊字符替换forbid)
            if (newBody.contains("forbid")) {
                Map<String, Object> maps = new HashMap<String, Object>();
                maps.put("msg", "参数中有特殊字符！");
                maps.put("code", 500);
                Gson gson = new Gson();
                ctx.setResponseBody(gson.toJson(maps));
                return null;
            }
            final byte[] reqBodyBytes = newBody.getBytes();
            ctx.setRequest(new HttpServletRequestWrapper(request) {
                @Override
                public ServletInputStream getInputStream() throws IOException {
                    return new ServletInputStreamWrapper(reqBodyBytes);
                }

                @Override
                public int getContentLength() {
                    return reqBodyBytes.length;
                }

                @Override
                public long getContentLengthLong() {
                    return reqBodyBytes.length;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Map<String, Object> cleanXSS(String value) {
        value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
        value = value.replaceAll("'", "& #39;");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("script", "");
        value = value.replaceAll("[*]", "[" + "*]");
        value = value.replaceAll("[?]", "[" + "?]");

        String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|%|chr|mid|master|truncate|" +
                "char|declare|sitename|net user|xp_cmdshell|;|or|+|,|like'|and|exec|execute|insert|create|drop|" +
                "table|from|grant|use|group_concat|column_name|" +
                "information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|" +
                "chr|mid|master|truncate|char|declare|or|;|--|,|like|//|/|%|#";

        Map<String, Object> mapjson = new HashMap<String, Object>();
        if (!StringUtils.isEmpty(value)) {
            JSONObject json = JSONObject.fromObject(value);
            String[] badStrs = badStr.split("\\|");
            Map<String, Object> map = json;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String value1 = entry.getValue().toString();
                String[] values = value1.split(" ");
                for (int i = 0; i < badStrs.length; i++) {
                    for (int j = 0; j < values.length; j++) {
                        if (values[j].equalsIgnoreCase(badStrs[i])) {
                            values[j] = "forbid";
                        }
                    }
                }

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < values.length; i++) {
                    if (i == values.length - 1) {
                        sb.append(values[i]);
                    } else {
                        sb.append(values[i] + " ");
                    }
                }
                value1 = sb.toString();
                mapjson.put(entry.getKey(), value1);
            }
        }
        return mapjson;
    }
}
